﻿Imports System.IO
Imports System.Net
Imports System.Web

Public Class FreeMusic

    Private Guid As String = ""
    Public Event DidProgress(ByVal percent As Integer)
    Public Event DidFetchDetails(ByVal songs)

#Region "Public functions"

    Public Sub Login()

        ' Make request
        Dim request As HttpWebRequest
        request = WebRequest.Create("http://login.vk.com/")
        request.Method = "GET"

        ' Set request settings
        request.Headers.Add(HttpRequestHeader.Cookie, "remixchk=5; l=5337115; p=7378d9d80df7781b3849bec8db9bf5cb61a4")

        ' Get response
        Dim response As HttpWebResponse = request.GetResponse
        Dim responseStream As StreamReader = New StreamReader(response.GetResponseStream, System.Text.Encoding.GetEncoding("iso-8859-5"))
        Dim result As String = responseStream.ReadToEnd
        result = TextBetween(result, "<input type='hidden' name='s' id='s' value='", "'")
        responseStream.Close()
        response.Close()

        ' Set the id
        Me.Guid = result

    End Sub

    Public Sub Login(ByVal Username As String, ByVal Password As String)

        ' Make request
        Dim request As HttpWebRequest
        request = WebRequest.Create("http://login.vk.com/?act=login")
        request.Method = "POST"

        ' Create POST content and send
        Dim postdata As String = "email=" & HttpUtility.UrlEncode(Username) & "&pass=" & HttpUtility.UrlEncode(Password) & "&expire=&vk="
        Dim postbytes() As Byte = System.Text.Encoding.UTF8.GetBytes(postdata)
        request.ContentType = "application/x-www-form-urlencoded"
        request.ContentLength = postbytes.Length
        Dim requestStream As Stream = request.GetRequestStream
        requestStream.Write(postbytes, 0, postbytes.Length)
        requestStream.Close()

        ' Get response
        Dim response As HttpWebResponse = request.GetResponse
        Dim responseStream As StreamReader = New StreamReader(response.GetResponseStream, System.Text.Encoding.GetEncoding("iso-8859-5"))
        Dim result As String = responseStream.ReadToEnd
        result = TextBetween(result, "<input type='hidden' name='s' value='", "'")
        responseStream.Close()
        response.Close()

        ' Set the id
        Me.Guid = result

    End Sub

    Public Function Search(ByVal s As String, Optional ByVal offset As Integer = 0) As List(Of Song)

        ' Make request
        Dim request As HttpWebRequest
        request = WebRequest.Create("http://vkontakte.ru/gsearch.php?section=audio&q=" & System.Web.HttpUtility.UrlEncode(s) & "&offset=" & offset)
        request.Method = "POST"

        ' Set request settings
        request.Headers(HttpRequestHeader.Cookie) = "remixchk=5; remixsid=" & Me.Guid & ";"

        ' Get response
        Dim response As HttpWebResponse = request.GetResponse
        Dim responseStream As StreamReader = New StreamReader(response.GetResponseStream, System.Text.Encoding.GetEncoding("iso-8859-5"))
        Dim result As String = responseStream.ReadToEnd
        responseStream.Close()
        response.Close()

        ' Parse songs
        Dim songs As List(Of Song) = ParseSongs(result)

        ' Return songs
        Return songs

    End Function

    Public Function FetchDetail(ByVal song As Song) As Song
        Try
            ' Make request
            Dim request As HttpWebRequest
            request = WebRequest.Create(song.Url)
            request.Method = "HEAD"

            ' Get response
            Dim response As HttpWebResponse = request.GetResponse
            Dim length As Integer = response.Headers("Content-Length")
            response.Close()

            ' Set details for song
            song.Size = length
            song.Bitrate = MapBitrate(Math.Round(song.Size * 8 / song.Duration / 1000))

            ' Return songs
            Return song
        Catch ex As Exception
            Return song
        End Try
    End Function

    Public Function FetchDetails(ByRef songs As List(Of Song)) As List(Of Song)
        If songs.Count = 0 Then Return songs
        Dim loopmax As Integer = songs.Count - 1

        ' Loop through all songs
        For i As Integer = 0 To loopmax
            Dim song As Song = songs(i)

            ' Make request
            Dim request As HttpWebRequest
            request = WebRequest.Create(song.Url)
            request.Method = "HEAD"

            ' Get response
            Dim response As HttpWebResponse = request.GetResponse
            Dim length As Integer = response.Headers("Content-Length")
            response.Close()

            ' Set details for song
            song.Size = length
            song.Bitrate = Math.Round(song.Size * 8 / song.Duration / 1000)

            ' Raise event for progress
            Dim progress As Integer = Math.Round(i / loopmax * 100)
            RaiseEvent DidProgress(progress)

            ' Raise event if finished
            If i = loopmax Then
                RaiseEvent DidFetchDetails(songs)
            End If
        Next

        ' Return songs
        Return songs

    End Function

#End Region

#Region "Private functions"

    Private Function ParseSongs(ByVal html As String) As List(Of Song)

        ' Create list for the results
        Dim songs As New List(Of Song)

        ' No songs will return the empty list
        If Not html.Contains("<div class=""audioRow""") Then
            Return songs
        End If

        ' Get the songs
        Dim splitter() As String = Split(html, "<div class=""audioRow""")
        Dim duplicate As Boolean
        For Each audioRow As String In splitter
            duplicate = False
            If audioRow.StartsWith(" id=""audio") Then
                Dim song As Song = GetSong(audioRow)

                ' If song is parsed correctly
                If Not IsNothing(song) Then

                    ' Check if song exists and add quantity
                    For Each s As Song In songs
                        If s.Url = song.Url Then
                            s.Quantity += 1
                            duplicate = True
                        End If
                    Next

                    ' If it does not exist, then add it
                    If Not duplicate Then
                        songs.Add(song)
                    End If
                End If

            End If
        Next

        ' Return result
        Return songs

    End Function

#End Region

#Region "Private helpers"

    Private Function GetSong(ByVal audioRow As String) As Song
        Dim song As New Song

        Dim operate As String
        operate = TextBetween(audioRow, "<img class=""playimg"" onclick=""return operate(", ");""")
        operate = operate.Replace("'", "")
        Dim fileinfo() As String = Split(operate, ",")

        If fileinfo.Count < 5 Then Return Nothing

        song.Artist = TextInTag(audioRow, "<b id=""performer", "</b>")
        song.Title = TextInTag(audioRow, "<span id=""title", "</span>")
        song.Duration = fileinfo(4)
        song.Url = "http://cs" & fileinfo(1) & ".vkontakte.ru/u" & fileinfo(2) & "/audio/" & fileinfo(3) & ".mp3"

        Return song
    End Function

    Private Function StripHtml(ByVal i As String) As String
        If i.Length < 1 Then Return ""
        i = i.Replace("<br/>", vbCrLf)
        Return System.Text.RegularExpressions.Regex.Replace(i, "<[^>]*>", String.Empty)
    End Function

    Private Function TextInTag(ByVal i As String, ByVal s As String, ByVal e As String, ByVal p As String) As String
        Dim result As String
        result = i.Substring(i.IndexOf(s) + s.Length)
        result = TextBetween(result, p, e)
        result = StripHtml(result)
        result = HttpUtility.HtmlDecode(result)
        Return result
    End Function

    Private Function TextInTag(ByVal i As String, ByVal s As String, ByVal e As String) As String
        Dim result As String
        result = i.Substring(i.IndexOf(s) + s.Length)
        result = TextBetween(result, """>", e)
        result = StripHtml(result)
        result = HttpUtility.HtmlDecode(result)
        Return result
    End Function

    Private Function TextBetween(ByVal i As String, ByVal s As String, ByVal e As String) As String
        Dim splitter() As String = Split(i, s, 2)
        splitter = Split(splitter(1), e, 2)
        Return splitter(0)
    End Function

    Private Function MapBitrate(ByVal bitrate As Integer)
        Dim tolerance As Integer = 10
        Dim mappings() As Integer = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 192, 224, 256, 320}
        For Each map As Integer In mappings
            If (bitrate * 0.9) < map And (bitrate * 1.1) > map Then
                Return map
            End If
        Next
        Return bitrate
    End Function

#End Region

#Region "Public properties"

    Public ReadOnly Property IsLoggedIn() As Boolean
        Get
            Return Guid.Length > 0
        End Get
    End Property

#End Region

    Public Class Song
        Public RowId As Integer = 0
        Public Quantity As Integer = 1
        Public Artist As String = "-"
        Public Title As String = "-"
        Public Duration As Integer = 0
        Public Size As Integer = 0
        Public Bitrate As Integer = 0
        Public Url As String = "-"
    End Class

End Class